/**
 * 
 */
package galacticthrone.screen.menu;

import galacticthrone.main.GTGame;
import galacticthrone.properties.Paths;

import javax.media.opengl.GL;

import javagame.core.io.Screen.ScreenRenderer;
import javagame.core.io.video.GLColor;
import javagame.core.io.video.texture.Texture;
import javagame.core.main.Temporal;
import javagame.core.main.Time;

// TODO Javadoc
/**
 * <br>
 *
 * @author Jaco van der Westhuizen
 */
public class StarField implements ScreenRenderer, Temporal
{
    private final static int NUM_STARS = 10000;
    private final static float WHITENESS = 0.9f; 
    private final static float MIN_Z = .01f; 
    private final static float MAX_Z = 32f;
    private final static float VAR_Z = MAX_Z - MIN_Z;
    private final static float START_STAR_SIZE = 256f;
    private final static float END_STAR_SIZE = 64f;
    private final static float STAR_SIZE_FADE_TIME = 10f;

    private boolean started = false;
    private Time.Absolute startTime;
    private float starSize = START_STAR_SIZE;

    private int width  = 800;
    private int height = 600;
    private float starSizeX = .01f;
    private float starSizeY = .01f;

    private final float[] starX = new float[NUM_STARS];
    private final float[] starY = new float[NUM_STARS];
    private final float[] starZ = new float[NUM_STARS];
    private final GLColor[] starC = new GLColor[NUM_STARS];
    
    private float velX;
    private float velY;
    private float velZ;
    
    private Texture starTex = null;

    public StarField(float velX, float velY, float velZ)
    {
        this.velX = -velX;
        this.velY = -velY;
        this.velZ = -velZ * MAX_Z;
        
        for (int i = 0; i < NUM_STARS; i++)
        {
            generateStar(i);
        }
    }

    /* (non-Javadoc)
     * @see javagame.core.io.Screen.ScreenRenderer#loadAssets(javax.media.opengl.GL, boolean)
     */
    @Override
    public void loadAssets(GL gl, boolean isInitial)
    {
        if (isInitial)
        {
        	gl.glEnable(GL.GL_TEXTURE_2D);
            starTex = GTGame.getTextureManager().grab(Paths.TEXTURES + "/menu/Star_menu.png");
        }
    }

    /* (non-Javadoc)
     * @see javagame.core.io.Screen.ScreenRenderer#onScreenReshape(int, int)
     */
    @Override
    public void onScreenReshape(int width, int height)
    {
        this.width  = width;
        this.height = height;
    }

    /* (non-Javadoc)
     * @see javagame.core.io.Screen.ScreenRenderer#renderForDisplay(javax.media.opengl.GL)
     */
    @Override
    public void renderForDisplay(GL gl)
    {
        if (!started)
        {
        	gl.glBegin(GL.GL_QUADS);
        	gl.glColor4f(1, 1, 1, 1);
        	gl.glVertex2f(-1, -1);
        	gl.glVertex2f(-1,  1);
        	gl.glVertex2f( 1,  1);
        	gl.glVertex2f( 1, -1);
        	gl.glEnd();
        	return;
        }

        if ((width == 0) || (height == 0) || (starTex == null))
            return;

        starTex.activate(gl);
        gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE_MINUS_SRC_ALPHA);

        gl.glBegin(GL.GL_QUADS);

        for (int i = 0; i < NUM_STARS; i++)
        {
            float z = starZ[i];
            float posX = MAX_Z * starX[i] / z; 
            float posY = MAX_Z * starY[i] / z; 
          
            gl.glColor3fv(GLColor.blend(GLColor.BLACK, starC[i], (1f - (z - MIN_Z) / VAR_Z)).getRGB(), 0);
            gl.glTexCoord2f(0, 0);
            gl.glVertex2f(posX - starSizeX, posY - starSizeY);
            gl.glTexCoord2f(0, 1);
            gl.glVertex2f(posX - starSizeX, posY + starSizeY);
            gl.glTexCoord2f(1, 1);
            gl.glVertex2f(posX + starSizeX, posY + starSizeY);
            gl.glTexCoord2f(1, 0);
            gl.glVertex2f(posX + starSizeX, posY - starSizeY);
        }
        
        gl.glEnd();
    }

    /* (non-Javadoc)
     * @see javagame.core.io.Screen.ScreenRenderer#renderForPicking(javax.media.opengl.GL)
     */
    @Override
    public void renderForPicking(GL gl)
    {
    }

    /* (non-Javadoc)
     * @see javagame.core.io.Screen.ScreenRenderer#unloadAssets(javax.media.opengl.GL, boolean)
     */
    @Override
    public void unloadAssets(GL gl, boolean isFinal)
    {
        if (isFinal)
        {
            GTGame.getTextureManager().release(gl, starTex);
            starTex = null;
        }
    }

    /* (non-Javadoc)
     * @see javagame.core.main.time.Temporal#update(javagame.core.main.time.Time)
     */
    @Override
    public void update(Time time)
    {
        if (!started)
        {
            started = true;
            startTime = time.getAbsolute();
        }
        else
        {
            float age = time.getElapsedFrom(startTime).getSeconds() / STAR_SIZE_FADE_TIME;

            if (age >= 1)
            {
                starSize = END_STAR_SIZE;
            }
            else
            {
                starSize = (float)(Math.pow(END_STAR_SIZE / START_STAR_SIZE, age) * START_STAR_SIZE);
            }
        }
        
        starSizeX = starSize / width;
        starSizeY = starSize / height;

        float delta = time.getElapsed().getSeconds();

        for (int i = 0; i < NUM_STARS; i++)
        {
            starX[i] += velX * delta;
            starY[i] += velY * delta;
            starZ[i] += velZ * delta;
            
            if (starX[i] < -1)
                starX[i] += 2;
            else if (starX[i] > 1)
                starX[i] -= 2;

            if (starY[i] < -1)
                starY[i] += 2;
            else if (starY[i] > 1)
                starY[i] -= 2;

            if (starZ[i] <= MIN_Z)
                starZ[i] += VAR_Z;
            else if (starZ[i] > MAX_Z)
                starZ[i] -= VAR_Z;
        }
    }

    private void generateStar(int index)
    {
        starX[index] = (float)(Math.random() * 2 - 1);
        starY[index] = (float)(Math.random() * 2 - 1);
        starZ[index] = (float)(Math.random() * VAR_Z  + MIN_Z);
        
        int i = (int)(Math.random() * 3);
        int j = (int)(Math.random() * 2);

        if (i == j)
            j++;

        starC[index] = new GLColor(((i == 0) ? 1f : ((j == 0) ? (float)(Math.random() * (1 - WHITENESS) + WHITENESS) : WHITENESS)),
                                   ((i == 1) ? 1f : ((j == 1) ? (float)(Math.random() * (1 - WHITENESS) + WHITENESS) : WHITENESS)),
                                   ((i == 2) ? 1f : ((j == 2) ? (float)(Math.random() * (1 - WHITENESS) + WHITENESS) : WHITENESS)));
    }
}
